﻿namespace Legend
{
    using System;
    using System.Collections.Generic;
    
    /// <summary>
    /// Provides functions for the Range{T} class.
    /// </summary>
    public class Range
    {
        /// <summary>
        /// Creates a new instance of Range(T) using the default comparer of T.
        /// </summary>
        /// <param name="lowerBound">The lowest value that is part of the range.</param>
        /// <param name="upperBound">The highest value that is part of the range.</param>
        /// <typeparam name="T">The type that represents the bounds of the range.</typeparam>
        /// <returns>A new range.</returns>
        public static Range<T> Create<T>(T lowerBound, T upperBound)
        {
            return new Range<T>(lowerBound, upperBound);
        }
    }

    /// <summary>
    /// Represents a range of values.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Range<T>
    {
        private IComparer<T> comparer;

        /// <summary>
        /// Creates a new instance of Range(T) using the default comparer of T.
        /// </summary>
        /// <param name="lowerBound">The lowest value that is part of the range.</param>
        /// <param name="upperBound">The highest value that is part of the range.</param>
        public Range(T lowerBound, T upperBound)
            : this(lowerBound, upperBound, Comparer<T>.Default)
        {
        }

        /// <summary>
        /// Creates a new instance of Range(T).
        /// </summary>
        /// <param name="lowerBound">The lowest value that is part of the range.</param>
        /// <param name="upperBound">The highest value that is part of the range.</param>
        /// <param name="comparer">The comparer that will be used to compare values in the range.</param>
        public Range(T lowerBound, T upperBound, IComparer<T> comparer)
        {
            this.comparer = comparer;

            this.AssertThatUpperBoundIsNotLowerThanLowerBound(lowerBound, upperBound);

            this.LowerBound = lowerBound;
            this.UpperBound = upperBound;
        }

        private void AssertThatUpperBoundIsNotLowerThanLowerBound(T lowerBound, T upperBound)
        {
            if (this.Compare(lowerBound, upperBound) > 0)
            {
                throw new ArgumentException("Upper bound must be equal to or greater than the lower bound.", "upperBound");
            }
        }

        /// <summary>
        /// The lowest value that is part of the range.
        /// </summary>
        public T LowerBound { get; private set; }

        /// <summary>
        /// The highest value that is part of the range.
        /// </summary>
        public T UpperBound { get; private set; }

        /// <summary>
        /// Gets a value indicating if the specified value is within the bound of the range.
        /// </summary>
        /// <param name="value">The value to test.</param>
        /// <returns>True if the value is in range.</returns>
        public bool IsInRange(int value)
        {
            return value.CompareTo(this.LowerBound) >= 0 && value.CompareTo(this.UpperBound) <= 0;
        }

        private int Compare(T first, T second)
        {
            return this.comparer.Compare(first, second);
        }
    }
}
